package com.cs.code.auto;

import com.alibaba.fastjson.JSON;
import com.cs.core.common.VarConst;
import com.cs.core.util.FileUtil;
import com.cs.core.util.ParamUtil;
import com.cs.core.util.StringUtil;
import com.cs.code.enums.NameType;
import com.cs.code.enums.TplType;
import com.cs.code.exception.CodeException;
import com.cs.code.helper.JdbcHelper;
import com.cs.code.model.CsName;
import com.cs.code.model.CsTable;
import com.cs.code.util.FtlUtil;
import com.cs.code.util.VmUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 *      自定义
 * </p>
 * @author wind
 * @date    2025/1/22 17:27
 * @version v1.0
 */
public class CustomHelper {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 模板目录
     */
    private String dir;

    /**
     * 模板输出目录
     */
    private String root;

    /**
     * 模板名称和生成文件名映射
     */
    private final Map<String, CsName> fileMap;

    /**
     * 数据库工具
     */
    private JdbcHelper jdbcHelper;

    /**
     * 包含Q
     */
    private final List<String> includeList;

    /**
     * 等于
     */
    private final List<String> equalList;

    /**
     * 排除
     */
    private final List<String> excludeList;

    /**
     * 表
     */
    private List<CsTable> tables;

    /**
     * 公共参数
     */
    private final Map<String, Object> globalMap;

    /**
     * 父及包名
     */
    private String parent;

    /**
     * 作者
     */
    private String author;

    /**
     * 测试
     */
    private boolean test;

    private CustomHelper(){
        this.excludeList = new ArrayList<>();
        this.includeList = new ArrayList<>();
        this.equalList = new ArrayList<>();
        this.fileMap = new HashMap<>();
        this.globalMap = new HashMap<>();
        this.parent = "";
        this.author = "";
        this.test = false;
    }

    /**
     * 参数校验
     */
    private void validate(){
        ParamUtil.notNull(jdbcHelper, "数据源未设置");
        ParamUtil.notNull(root, "模板输出根目录未设置");
    }

    /**
     * 执行模板
     * @param name
     * @param nameModel 代码生成
     */
    public void execute(String name, CsName nameModel){
        this.tables.forEach(t -> {
            execute(name, nameModel, t);
        });
    }

    /**
     * 获取表数量
     * @return
     */
    public int getTableCount(){
        return this.tables.size();
    }

    /**
     * 获取表数量
     * @return
     */
    public int getTplCount(){
        return this.fileMap.size();
    }

    /**
     * 执行模板
     */
    public int execute(){
        return execute(false);
    }

    /**
     * 执行模板
     */
    public int execute(boolean clear){
        if(clear){
            FileUtil.delDir(this.root);
        }
        AtomicInteger count = new AtomicInteger();
        this.tables.forEach(t -> {
            this.fileMap.forEach((k, v) -> {
                if(execute(k, v, t)){
                    count.getAndIncrement();
                }
            });
        });
        return count.get();
    }

    /**
     * 生成
     * @param tplName
     * @param data
     */
    private boolean execute(String tplName, CsName nameModel, CsTable data){
        try {
            ParamUtil.notEmpty(tplName, "模板名称不能为空");
            ParamUtil.notNull(nameModel, "命名模型不能为空");
            String module = nameModel.getModule();
            String filepath = this.root;
            if(!this.root.endsWith(VarConst.FILE_SEPARATOR)){
                filepath += VarConst.FILE_SEPARATOR;
            }

            /**
             * 测试不生成包名，模块多级目录结构
             */
            if(!test){
                if(parent != null && parent.contains(VarConst.POINT_STR)){
                    filepath += String.join(VarConst.FILE_SEPARATOR, parent.split("\\.")) + VarConst.FILE_SEPARATOR;
                }
                if(module.contains(VarConst.POINT_STR)){
                    filepath += String.join(VarConst.FILE_SEPARATOR, module.split("\\."));
                }else{
                    filepath += module;
                }
            }
            filepath += VarConst.FILE_SEPARATOR;
            Path path = Paths.get(filepath);
            boolean sign = Files.exists(path);
            if(!sign){
                Files.createDirectories(path);
            }
            ParamUtil.isThrow(!Files.isDirectory(path), "代码生成目录必须是文件夹");

            String filename = filepath;
            String name = nameModel.getName();
            /**
             * 如果指定name,则输出文件名直接以此命名
             */
            if(StringUtil.isNotBlank(name)){
                filename += name;
            }else{
                String prefix = nameModel.getPrefix();
                String suffix = nameModel.getSuffix();
                NameType nameType = nameModel.getNameType();
                String fileType = nameModel.getFileType();
                String tmpName = suffix + VarConst.POINT_STR + fileType;
                if(NameType.DEFAULT == nameType){
                    tmpName = data.getName() + tmpName;
                }else if(NameType.LOWER_CAMEL == nameType){
                    tmpName = data.getAlias() + tmpName;
                }else{
                    tmpName = data.getProperty() + tmpName;
                }
                filename += (prefix + tmpName);
            }
            Map<String, Object> map = JSON.parseObject(JSON.toJSONString(data), Map.class);
            if(StringUtil.isNotBlank(module)){
                if(StringUtil.isNotBlank(parent)){
                    map.put(VarConst.PACKAGE, this.parent + VarConst.POINT_STR + module);
                }else{
                    map.put(VarConst.PACKAGE, module);
                }
            }else{
                map.put(VarConst.PACKAGE, this.parent);
            }
            map.put(VarConst.PARENT, this.parent);
            map.put(VarConst.AUTHOR, this.author);
            map.putAll(nameModel.getMap());
            map.putAll(this.globalMap);
            if(tplName.endsWith(TplType.FREEMARKER.getSuffix())){
                FtlUtil.out(dir, tplName, filename, map);
            }else if(tplName.endsWith(TplType.VELOCITY.getSuffix())){
                VmUtil.out(dir, tplName, filename, map);
            }else{
                throw new CodeException("当前模板不支持");
            }
            return true;
        } catch (Exception e) {
            logger.error("CustomHelper.execute failed, ere is {}", e.getMessage());
            throw new CodeException("模板生成失败, 原因:" + e.getMessage());
        }
    }

    /**
     * 创建builder构建器
     * @return
     */
    public static CustomHelper.Builder newBuilder(){
        return new CustomHelper.Builder();
    }

    /**
     * builder构建器
     */
    public static class Builder {

        private final CustomHelper customHelper;

        public Builder() {
            this.customHelper = new CustomHelper();
        }

        public CustomHelper.Builder source(String jdbcUrl, String user, String password){
            this.customHelper.jdbcHelper = new JdbcHelper(jdbcUrl, user, password);
            return this;
        }


        public CustomHelper.Builder test(){
            this.customHelper.test = true;
            return this;
        }

        /**
         * 模板目录和模板输出根目录配置
         * @param dir
         * @param root
         * @return
         */
        public CustomHelper.Builder out(String dir, String root){
            if(StringUtil.isNotBlank(dir)){
                this.customHelper.dir = dir;
            }
            if(StringUtil.isNotBlank(root)){
                this.customHelper.root = root;
            }
            return this;
        }

        public CustomHelper.Builder root(String root){
            this.customHelper.root = root;
            return this;
        }

        public CustomHelper.Builder dir(String dir){
            this.customHelper.dir = dir;
            return this;
        }

        /**
         * 设置父包名
         * @param parent
         * @return
         */
        public CustomHelper.Builder parent(String parent){
            if(StringUtil.isNotBlank(parent)){
                this.customHelper.parent = parent;
            }
            return this;
        }

        /**
         * 设置作者
         * @param author
         * @return
         */
        public CustomHelper.Builder author(String author){
            if(StringUtil.isNotBlank(author)){
                this.customHelper.author = author;
            }
            return this;
        }

        public CustomHelper.Builder template(String name, CsName nameModel){
            this.customHelper.fileMap.put(name, nameModel);
            return this;
        }

        public CustomHelper.Builder template(Map<String, CsName> map){
            if(map != null){
                this.customHelper.fileMap.putAll(map);
            }
            return this;
        }

        /**
         * 包含
         * @param include
         * @return
         */
        public CustomHelper.Builder include(String... include){
            this.customHelper.includeList.addAll(Arrays.asList(include));
            return this;
        }

        /**
         * 等值
         * @param table
         * @return
         */
        public CustomHelper.Builder equal(String... table){
            this.customHelper.equalList.addAll(Arrays.asList(table));
            return this;
        }

        /**
         * 排除
         * @param exclude
         * @return
         */
        public CustomHelper.Builder exclude(String... exclude){
            this.customHelper.excludeList.addAll(Arrays.asList(exclude));
            return this;
        }

        /**
         * 全局配置
         * @param key
         * @param values
         * @return
         */
        public CustomHelper.Builder global(String key, Object values){
            this.customHelper.globalMap.put(key, values);
            return this;
        }

        /**
         * 全局配置
         * @param globalMap
         * @return
         */
        public CustomHelper.Builder global(Map<String, Object> globalMap){
            this.customHelper.globalMap.putAll(globalMap);
            return this;
        }

        /**
         * 构建
         * @return
         */
        public CustomHelper build(){
            this.customHelper.validate();
            List<CsTable> tables = this.customHelper.jdbcHelper.getTables();
            List<String> excludeList = this.customHelper.excludeList;
            List<String> includeList = this.customHelper.includeList;
            List<String> equalList = this.customHelper.equalList;
            if(excludeList.isEmpty() && includeList.isEmpty() && equalList.isEmpty()){
                this.customHelper.tables = tables;
            }else{
                this.customHelper.tables = tables.stream().filter(t -> {
                    boolean flag = excludeList.stream().anyMatch(s -> t.getName().contains(s));
                    if(flag){
                        return false;
                    }
                    flag = equalList.stream().anyMatch(s -> t.getName().equals(s));
                    if(flag){
                        return true;
                    }
                    return includeList.stream().anyMatch(s -> t.getName().contains(s));
                }).collect(Collectors.toList());
            }
            return this.customHelper;
        }

    }
}
